//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"

#define EV_GEOMETRY_DWS_MAX (1+1+1+1+1\
+(1+WL_OUTPUT_GEOMETRY_MANUFACTURER_STR_DWS_MAX)\
+(1+WL_OUTPUT_GEOMETRY_MODEL_STR_DWS_MAX)+1)
s8 wl_output_ev_geometry(struct client *c,u32 output_id,s32 x,s32 y,
                 s32 physical_width,s32 physical_height,s32 subpixel,void *make,
                                                      void *model,s32 transform)
{
  l r0=LWL_OK;
  /*--------------------------------------------------------------------------*/
  u8 *make_ascii=(u8*)make;
  u16 make_dws=0;
  u64 make_len=0;//without the 0 terminating char
  if(make&&make_ascii[0]!=0){
    u64 r=strnlen(&make_len,make,
                     WL_OUTPUT_GEOMETRY_MANUFACTURER_STR_DWS_MAX*sizeof(u32)-1);
    if(!r){
      PERR("client(%d):fatal:output:geometry manufacturer description too"
                                                               " long\n",c->so);
      r0=LWL_ERR;goto exit;
    }
    if(make_len) make_dws=(u16)DWS_ROUNDUP(make_len+1);
  }
  /*--------------------------------------------------------------------------*/
  u8 *model_ascii=(u8*)model;
  u16 model_dws=0;
  u64 model_len=0;//without the 0 terminating char
  if(model&&model_ascii[0]!=0){
    u64 r=strnlen(&model_len,model,WL_OUTPUT_GEOMETRY_MODEL_STR_DWS_MAX
                                                                *sizeof(u32)-1);
    if(!r){
      PERR("client(%d):fatal:output:geometry model description too long\n",
                                                                         c->so);
      r0=LWL_ERR;goto exit;
    }
    if(model_len) model_dws=(u16)DWS_ROUNDUP(model_len+1);
  }
  /*--------------------------------------------------------------------------*/
  
  u32 ev[EV_GEOMETRY_DWS_MAX];

  ev[0]=output_id;
  u16 ev_sz=(1+1+1+1+1+(1+make_dws)+(1+model_dws)+1)*sizeof(u32);
  ev[1]=ev_sz<<15|WL_OUTPUT_GEOMETRY;
  ev[2]=x;
  ev[3]=y;
  ev[4]=physical_width;
  ev[5]=physical_height;
  ev[6]=subpixel;
  ev[7]=make_len+1;
  if(make_dws) strncpy(&ev[8],make,make_dws*sizeof(u32));
  ev[8+make_dws]=model_len+1;
  if(model_dws)
    strncpy(&ev[8+make_dws+1],model,model_dws*sizeof(u32));
  ev[8+make_dws+1+model_dws]=transform;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):output::geometry output_id=%u x=%d y=%d"
                     " physical_width=%d physical_height=%d subpixel=%d make=%s"
                                 " model=%s transform=%d\n",c->so,output_id,x,y,
                                   physical_width,physical_height,subpixel,make,
                                                               model,transform);

  while(1){r0=sendmsg(c->so,&msg,0); if(r0!=-EINTR) break;}

  if(ISERR(r0)){
    PERR("client(%d):fatal(%ld):output::geometry unable to send event\n",
                                                                      c->so,r0);
    r0=LWL_ERR;goto exit;
  }

  if(r0!=ev_sz){
    PERR("client(%d):fatal:output::geometry only %ld/%ld were sent\n",c->so,r0,
                                                                         ev_sz);
    r0=LWL_ERR;goto exit;
  }

  r0=LWL_OK;

exit:
  return (s8)r0;
}

#define EV_MODE_DWS 6
s8 wl_output_ev_mode(struct client *c,u32 output_id,u32 flags,s32 width,
                                                         s32 height,s32 refresh)
{
  u32 ev[EV_MODE_DWS];

  ev[0]=output_id;
  u16 ev_sz=EV_MODE_DWS*sizeof(u32);
  ev[1]=ev_sz<<15|WL_OUTPUT_MODE;
  ev[2]=flags;
  ev[3]=width;
  ev[4]=height;
  ev[5]=refresh;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):output::mode output_id=%u flags=0x%08x width=%d"
                   " height=%d refresh=%d\n",c->so,output_id,flags,width,height,
                                                                       refresh);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):output::mode unable to send event\n",c->so,r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:output::mode only %ld/%ld were sent\n",c->so,r,
                                                                         ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}
